﻿using System;
using System.Configuration;
using System.Web;
using System.Web.Mvc;
using Facebook.API;

namespace Facebook.Web
{
    /// <summary>
    /// This class is responsible for persisting 
    /// the facebook configuration of your 
    /// application.
    /// </summary>
    public class Settings
    {
        public enum CanvasTypes { IFRAME, FBML }
        private readonly FacebookAPI facebookApi = new FacebookAPI();
        private readonly Config config = ConfigurationManager.GetSection("FacebookConfig") as Config;

        /// <summary>
        /// This method sets up the FacebookAPI and returns it fully initialized.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="canvasType"></param>
        /// <returns></returns>
        public FacebookAPI GetFacebookAPI(ActionExecutingContext context, CanvasTypes canvasType)
        {
            HttpContext                         = context.HttpContext; //<-- This line is the key to most of how this works...
            CanvasType                          = canvasType; //<-- This is the canvas type of the application, FBML or IFRAME
            
            
            /*
             * Basic API Setup
             */

            facebookApi.ApplicationKey          = config.ApplicationKey; 
            if(string.IsNullOrEmpty(facebookApi.ApplicationKey)) {throw new Exception("You must specify the Application Key in the web.config file of your Facebook Application");}
            facebookApi.Secret                  = config.SecretKey;
            if (string.IsNullOrEmpty(facebookApi.Secret)) { throw new Exception("You must specify the Secret in the web.config file of your Facebook Application"); }
            facebookApi.IsDesktopApplication    = false;
            facebookApi.SessionKey              = String.IsNullOrEmpty(facebookApi.SessionKey) ? GetSessionKey() : facebookApi.SessionKey;
            facebookApi.UserId                  = String.IsNullOrEmpty(facebookApi.UserId) ? GetUserId() : facebookApi.UserId;




            if (!string.IsNullOrEmpty(GetAuthToken()))
            {
                facebookApi.CreateSession(GetAuthToken());
                SaveSession(facebookApi.SessionKey, facebookApi.UserId);
            }
            else if(string.IsNullOrEmpty(facebookApi.SessionKey))
            {
                CanvasRedirect(config.LoginUrl);
            }
            
            //By this point we should have the facebookAPI filled in...
            AddApplication();

            return facebookApi;
        }
        
        #region ~~~~~~~~~~ SETTING PROPERTIES ~~~~~~~~~~
        /// <summary>
        /// The HttpContext is important because with out it we wouldn't have access
        /// to the Session, QueryString, Request, Response...etc.
        /// </summary>
        public HttpContextBase HttpContext { get;set; }

        /// <summary>
        /// The Type of Facebook Canvas being used, either IFRAME or FBML
        /// </summary>
        public CanvasTypes CanvasType {get;set; }
        #endregion

        #region ~~~~~~~~~ QUERY STRING PROPERTIES ~~~~~~~~~~

        /// <summary>
        /// Indicates if the page is in a FBML page.
        /// </summary>
        public string InCanvas
        {
            get
            {
                if (HttpContext.Request.QueryString[config.qskInCanvas] != null)
                    return HttpContext.Request.QueryString[config.qskInCanvas];
                else return "";
            }
        }

        /// <summary>
        /// Indicates if this is an IFrame app.
        /// </summary>
        public bool InIframe
        {
            get
            {
                if (HttpContext.Request.QueryString[config.qskInFrame] != null)
                    if (HttpContext.Request.QueryString[config.qskInFrame] == "1") { return true; }

                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool IsAdded
        {
            get
            {
                if (HttpContext.Request.QueryString[config.qskAdded] != null)
                    if (HttpContext.Request.QueryString[config.qskAdded] == "1") { return true; }

                return false;
            }
        }

        #endregion

        #region ~~~~~~~~~~ FACEBOOK IDENTIFICATION METHODS ~~~~~~~~~~
        /// <summary>
        /// Gets the current logged in user of the Facebook App
        /// </summary>
        /// <returns></returns>
        public string GetUserId()
        {
            string userid = "";

            if(CanvasType == CanvasTypes.IFRAME)
            {
                //Checking for request object...
                if (HttpContext.Request == null)
                { throw new Exception("Unable to get token because the HttpRequest is not set."); }
                // Check the QueryString for the User ID
                if (HttpContext.Request.QueryString[config.qskUser] == null)
                {
                    //No user id on QueryString you'll need to look at the Session or Cookies
                    userid = String.IsNullOrEmpty(GetFromSession(config.skUser)) ? "" : GetFromSession(config.skUser);
                }
                else
                {   //If we have a user id on the query string we'll use it...
                    userid = HttpContext.Request.QueryString[config.qskUser];
                }
                SaveToSession(config.skUser, userid);
            }
            else if (CanvasType == CanvasTypes.FBML)
            {
                userid = (string.IsNullOrEmpty(facebookApi.UserId))
                             ? HttpContext.Request[config.qskUser]
                             : facebookApi.UserId;
            }


            return userid;
        }
        /// <summary>
        /// Gets the session key for the Facebook App
        /// </summary>
        /// <returns>Session Key</returns>
        public string GetSessionKey()
        {
            string sessionkey = "";

            if (CanvasType == CanvasTypes.IFRAME)
            {
                //Try the request first...
                //then if we are using sessions try the session, else try the cookie
                if (HttpContext.Request == null)
                { throw new Exception("Unable to get token because the HttpContext is not set."); }
                if (HttpContext.Request.QueryString[config.qskSessionKey] == null)
                {
                    //throw new Exception("No AuthToken set in QueryString");
                    sessionkey = String.IsNullOrEmpty(GetFromSession(config.skSessionKey)) ? "" : GetFromSession(config.skSessionKey);
                }
                else
                {
                    sessionkey = HttpContext.Request.QueryString[config.qskSessionKey];
                }
                SaveToSession(config.skSessionKey, sessionkey);
            }
            else if (CanvasType == CanvasTypes.FBML)
            {
                sessionkey = (string.IsNullOrEmpty(facebookApi.SessionKey))
                                 ? HttpContext.Request[config.qskSessionKey]
                                 : facebookApi.SessionKey;
            }

            return sessionkey;
        }
        /// <summary>
        /// Returns the AuthToken if available.
        /// </summary>
        /// <returns></returns>
        public string GetAuthToken()
        {
            if (HttpContext.Request == null)
            {   throw new Exception("Unable to get token because the HttpRequest is not set.");         }
            return HttpContext.Request.QueryString[config.qskAuthToken];
        }
        #endregion

        #region ~~~~~~~~~~ SESSION PRESISTANCE METHODS ~~~~~~~~~~
        /// <summary>
        /// Method is used to save key value pairs to the session or in a cookie...
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        private void SaveToSession(string key, string value)
        {
            if (true) //UseSession)
            {   
                if (HttpContext.Session == null)
                { throw new Exception("You must set the HttpContext of the FacebookConfig oject in order to access the Session"); }
                HttpContext.Session[key] = value;
            }
            else
            {
                if (HttpContext.Response == null)
                {
                    throw new Exception(
                        "You must set the HttpContext of the FacebookConfig object in order to save cookies!");
                }
                HttpContext.Response.Cookies.Clear();
                HttpContext.Response.Cookies.Add(new HttpCookie(key, value));
            }
        }

        /// <summary>
        /// Retrieves a value from the session...
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private string GetFromSession(string key)
        {
            try
            {
                if (config.UseSession)
                {
                    return HttpContext.Session[key].ToString();
                }
                else
                {
                    return HttpContext.Request.Cookies[key].Value;
                }
            }
            catch (NullReferenceException)
            { }
            return null;
        }

        /// <summary>
        /// This method persists the Session Key and User Id to 
        /// either a cookie or the HttpSession.  I was going to remove it but decided 
        /// that is does serve some purpose...
        /// </summary>
        /// <param name="sessionKey"></param>
        /// <param name="userId"></param>
        private void SaveSession(string sessionKey, string userId)
        {
            if (CanvasType == CanvasTypes.IFRAME)
            {
                if (config.UseSession)
                {
                    if (HttpContext.Session == null)
                    {
                        throw new Exception(
                            "You must set the HttpContext of the FacebookConfig oject in order to access the Session");
                    }
                    HttpContext.Session[config.skUser] = userId;
                    HttpContext.Session[config.skSessionKey] = sessionKey;
                }
                else
                {
                    if (HttpContext.Response == null)
                    {
                        throw new Exception(
                            "You must set the HttpContext of the FacebookConfig object in order to save cookies!");
                    }
                    HttpContext.Response.Cookies.Clear();
                    HttpContext.Response.Cookies.Add(new HttpCookie(config.skUser, userId));
                    HttpContext.Response.Cookies.Add(new HttpCookie(config.skSessionKey, sessionKey));
                }
            }
        }

        /// <summary>
        /// This method removes the Session Key and User ID from the 
        /// HttpSession or Cookies
        /// </summary>
        private void ClearSession()
        {
            if (config.UseSession)
            {
                try
                {
                    if (HttpContext.Session == null)
                    {
                        throw new Exception(
                            "You must set the HttpContext of the FacebookConfig oject in order to access the Session");
                    }
                    HttpContext.Session[config.skSessionKey] = null;
                    HttpContext.Session[config.skUser] = null;
                }
                catch (System.NullReferenceException ex)
                {
                    //null reference...
                }
            }
            else
            {
                if (HttpContext.Response == null)
                {
                    throw new Exception(
                        "You must set the HttpContext of the FacebookConfig object in order to save cookies!");
                }
                HttpContext.Response.Cookies.Clear();
            }
        }

        #endregion

        #region ~~~~~~~~~~ REDIRECTION METHODS ~~~~~~~~~~
        /// <summary>
        /// This method performs generic redirection of IFRAMEs or FBML pages.  The Config CanvasParam is added to the url here, if you want to disable set the CANVASPARAM value in teh web.config file = to ""
        /// </summary>
        /// <param name="url"></param>
        private void CanvasRedirect(string url)
        {
            if (CanvasType == CanvasTypes.IFRAME)
            {
                HttpContext.Response.Write("<script type=\"text/javascript\">\n" +
                                           "if (parent != self) \n" +
                                           "top.location.href = \"" + url + @"&v=1.0" + config.CanvasParam + "\";\n" +
                                           "else self.location.href = \"" + url + @"&v=1.0" + config.CanvasParam + "\";\n" +
                                           "</script>");
            }
            else if (CanvasType == CanvasTypes.FBML)
            { 
                HttpContext.Response.Write("<fb:redirect url=\"" + url + @"&v=1.0" + config.CanvasParam + "\"/>"); 
            }
            HttpContext.Response.End();
        }

        /// <summary>
        /// This method adds your facebook application ot the user if needed
        /// </summary>
        private void AddApplication()
        {
            if (!facebookApi.IsAppAdded() && config.AutoAddApp)
            {
                if (CanvasType == CanvasTypes.IFRAME)
                {
                    if (facebookApi.SessionKey != null)
                    {
                        HttpContext.Response.Cookies.Clear();
                        CanvasRedirect(config.AddUrl);
                    }
                    else
                    {
                        CanvasRedirect(config.LoginUrl);
                    }
                }
                else if (CanvasType == CanvasTypes.FBML)
                {

                    //TODO: Come back and review this section
                    if ((!string.IsNullOrEmpty(InCanvas) && InCanvas.Equals("1")) ||
                    (!string.IsNullOrEmpty(HttpContext.Request.Url.ToString()) && HttpContext.Request.Url.ToString().ToLower().Contains("facebook.com")))
                    {
                        CanvasRedirect(config.AddUrl);
                    }
                    else
                    {
                        HttpContext.Response.Redirect(config.AddUrl + @"&v=1.0");
                    }
                }
            }
        }

        #endregion
    }
}
